package cc.rengu.igas.opms.core.packswitch;

import cc.rengu.igas.opms.common.constant.OpmsTreeNodeConstant;
import cc.rengu.igas.opms.common.constant.OpmsParamConstant;
import cc.rengu.igas.opms.common.enums.RespCodeEnum;
import cc.rengu.igas.share.common.constant.UserSessionConstant;
import cc.rengu.igas.share.core.realize.UserService;
import cc.rengu.igas.share.core.realize.impl.UserServiceImpl;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.MchntOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.PayOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.SrcTxnAuthCfgMapper;
import cc.rengu.oltp.service.common.dao.UserOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.MchntOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.PayOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SrcTxnAuthCfgMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.UserOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.common.enums.OltpRpcdEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.TxnInfo;
import cc.rengu.oltp.service.realize.SensitiveInfoService;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.TxnInfoService;
import cc.rengu.oltp.service.realize.impl.SensitiveInfoServiceImpl;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.service.realize.impl.TxnInfoServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 与线上收银台WEB服务器通讯层报文解包前后、组前包后实现类
 * Created by 王成 on 2018/6/1.
 */
public class OpmWebServerPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {
    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /*健康检查*/
        String healthChekUrl = xmlTreeUtil.getXmlTreeStringValue("_MESSAGEHEAD/@server_url");
        if (!StringUtil.isEmptyOrNull(healthChekUrl) && (healthChekUrl.contains("healthCheck") || healthChekUrl.contains("nginx.html"))) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID, AppParamConstant.DEFAULT_INSTID);
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_SRC_SYS_ID, "NGINX");
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "HEALCHTK");
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, AppParamConstant.DEFAULT_BIZ_TYPE);
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_CHANNEL_ID, "NGINX");
            return "{}".getBytes();
        }
        String jsonStr = new String(bytes);
        /* 转换XSS攻击的字符 */
        String signBlockString = generateSignBlockString(jsonStr);
        jsonStr = jsonStr.replaceAll("<", "＜").replaceAll(">", "＞");
        xmlTreeUtil.setXmlTreeStringValue(OpmsTreeNodeConstant.SIGNBLOCK, signBlockString);
        return jsonStr.getBytes();
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-Type", "application/json;charset=utf-8");
        xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/ArgList", "Content-Type");
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        String instId = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.INST_ID);
        String msgSrcId = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.SRC_SYS_ID);
        String txnNum = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.TXN_NUM);
        String bizType = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.BIZ_TYPE);
        String chnlId = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.CHANNEL_ID);
        String sessionId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SESSION);
        /*普通退款交易特殊处理*/
        if (OpmsParamConstant.REFUND_PUB_TXN_NUM.equals(txnNum) && AppParamConstant.DEFAULT_BIZ_TYPE.equals(bizType)) {
            xmlTreeUtil.setXmlTreeStringValue("OPMS_REFUND_TXN_NUM", txnNum);
            String mchntOrderId = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.MCHNT_ORDER_ID);
            /*根据商户订单号查询原支付交易*/
            UserOrderInfoMapper userOrderInfoMapper = new UserOrderInfoMapperImpl();
            UserOrderInfo userOrderInfo = userOrderInfoMapper.selectUserOrderInfoByMchntOrderId(instId, mchntOrderId, msgSrcId, chnlId);
            if (userOrderInfo == null) {
                //线下微信支付宝扫码支付的时候,交易渠道特定为 OPMS
                userOrderInfo = userOrderInfoMapper.selectUserOrderInfoByMchntOrderId(instId, mchntOrderId, msgSrcId, OpmsParamConstant.OPM_SYS_ID);
                if (null == userOrderInfo) {
                    rglog.info("用户支付订单不存在");
                }
                rglog.info("用户订单不存在,不转换交易码");
                return 0;
            }
            MchntOrderInfoMapper mchntOrderInfoMapper = new MchntOrderInfoMapperImpl();
            List<MchntOrderInfo> mchntOrderInfoList = mchntOrderInfoMapper.selectMchntOrderInfoBySysSeqNum(instId, userOrderInfo.getSysSeqNum());
            if (mchntOrderInfoList == null || mchntOrderInfoList.size() == 0) {
                rglog.info("商户订单信息表中无该笔订单,instId:<{}>,sysSeqNum:<{}>.不转换交易码", instId, userOrderInfo.getSysSeqNum());
                return 0;
            }
            /*
             * 转换交易码
             * 无跳转快捷支付，无跳转签约支付，无跳转首次签约并支付
             * 新网银支付
             * 常规通道支付
             */
            if ("P01PM001".equals(mchntOrderInfoList.get(0).getTxnNum()) || "P01PM011".equals(mchntOrderInfoList.get(0).getTxnNum()) || "P01PM013".equals(mchntOrderInfoList.get(0).getTxnNum())) {
                /*无跳转退款交易码转换*/
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, "P04PM004");
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "P04PM004");
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, mchntOrderInfoList.get(0).getBizType());
                /*获取支付订单列表*/
                PayOrderInfoMapper payOrderInfoMapper = new PayOrderInfoMapperImpl();
                List<PayOrderInfo> payOrderInfoList = payOrderInfoMapper.selectPayOrderInfoBySysSeqNum(instId, mchntOrderInfoList.get(0).getSysSeqNum());
                if (payOrderInfoList == null || payOrderInfoList.size() == 0) {
                    rglog.error("根据机构号<{}>、系统流水号<{}>获取支付订单列表失败", instId, mchntOrderInfoList.get(0).getSysSeqNum());
                } else {
                    List<PayOrderInfo> payOrderInfoListNcqs = payOrderInfoList.stream().filter(item -> ChannelEnum.NCQS.getChannelType().equals(item.getDstChannelType())).collect(Collectors.toList());
                    List<PayOrderInfo> payOrderInfoListUpac = payOrderInfoList.stream().filter(item -> item.getMsgDstId().equals(ChannelEnum.UPAC.getChannelType())).collect(Collectors.toList());
                    if (!payOrderInfoListNcqs.isEmpty()) {
                        /*无卡快捷*/
                        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, "02");
                        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "02");
                    } else if (!payOrderInfoListUpac.isEmpty()) {
                        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, "01");
                        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "01");
                    }
                }
            } else if ("P01PM018".equals(mchntOrderInfoList.get(0).getTxnNum())) {
                //新网银退款交易码
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, "P04PM011");
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "P04PM011");
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, mchntOrderInfoList.get(0).getBizType());
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, mchntOrderInfoList.get(0).getBizType());
            } else {
                char[] convTxnNum = mchntOrderInfoList.get(0).getTxnNum().toCharArray();
                /*第三位从1->4代表退款*/
                convTxnNum[2] = '4';
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, new String(convTxnNum));
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, new String(convTxnNum));
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, mchntOrderInfoList.get(0).getBizType());
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, mchntOrderInfoList.get(0).getBizType());
            }
        }

        /* 获取交易配置信息 */
        TxnInfoService txnInfoService = new TxnInfoServiceImpl();
        TxnInfo txnInfo = txnInfoService.getCurrentTxnInfo();
        xmlTreeUtil.setXmlTreeObjectValue(TreeNodeConstant.TXN_INFO,txnInfo);

        /* 敏感信息解密及脱敏处理 */
        UserService userService = new UserServiceImpl();
        SensitiveInfoService sensitiveInfoService = new SensitiveInfoServiceImpl();
        if (!StringUtil.isEmptyOrNull(sessionId)) {
            sensitiveInfoService.decrypt(userService.getUserSessionValue(UserSessionConstant.SENSITIVE_KEY), null);
        }
        sensitiveInfoService.recovery(sessionId);

        //签名验签
        if (AppParamConstant.YES.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())) {
            String signBlockString = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.SIGNBLOCK);
            String signature = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SIGNATURE);
            String signatureKey = userService.getUserSessionValue(UserSessionConstant.SIGNATURE_KEY);
            /*收银台发起查询redis获取工作密钥*/
            if (sessionId == null || "".equals(sessionId.trim())) {
                rglog.error("sessionId为空，验签不通过");
                xmlTreeUtil.setXmlTreeStringValue(OpmsTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
                return 0;
            }
            xmlTreeUtil.setXmlTreeStringValue(OpmsTreeNodeConstant.WORK_KEY, userService.getUserSessionValue(UserSessionConstant.SIGNATURE_KEY));
            if (!MD5Util.verifySignString(signature, signBlockString, "&key=", signatureKey)) {
                rglog.error("验签失败,signature:<{}>,签名验签密钥:<{}>,待进行签名验签计算的blockString为::<{}>", signature, signatureKey, signBlockString);
                xmlTreeUtil.setXmlTreeStringValue(OpmsTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
            }
        }
        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        if (xmlTreeUtil.isExist("OPMS_REFUND_TXN_NUM")) {
            xmlTreeUtil.copyStringValue("OPMS_REFUND_TXN_NUM", TreeNodeConstant.TXN_NUM);
            xmlTreeUtil.copyStringValue("OPMS_REFUND_TXN_NUM", TreeNodeConstant.HEADER_TXN_NUM);
            xmlTreeUtil.setXmlTreeStringValue(OpmsTreeNodeConstant.BIZ_TYPE, AppParamConstant.DEFAULT_BIZ_TYPE);
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, AppParamConstant.DEFAULT_BIZ_TYPE);
        }
        /* 敏感信息加密及脱敏处理 */
        UserService userService = new UserServiceImpl();
        String instId = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.INST_ID);
        String sessionId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SESSION);
        SensitiveInfoService sensitiveInfoService = new SensitiveInfoServiceImpl();
        if (!StringUtil.isEmptyOrNull(sessionId)) {
            sensitiveInfoService.encrypt(userService.getUserSessionValue(UserSessionConstant.SENSITIVE_KEY), null);
        }
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(instId, OpmsParamConstant.OPMS_SYS_PARAM, OpmsParamConstant.SESSION_EXPIRY_TIMES);
        sensitiveInfoService.desensitise(sessionId, sysParam.getParamValue());
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        TxnInfo txnInfo = (TxnInfo) xmlTreeUtil.getXmlTreeObjectValue(TreeNodeConstant.TXN_INFO, TxnInfo.class);
        if (null == txnInfo) {
            TxnInfoService txnInfoService = new TxnInfoServiceImpl();
            txnInfo = txnInfoService.getCurrentTxnInfo();
        }
        if (txnInfo.getSrcTxnAuthCfg().getTxnNum().equals("HEALCHTK")){
            return new byte[0];
        }
        if (AppParamConstant.NO.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())) {
            return bytes;
        }
        String jsonBefore = new String(bytes);
        String signBlock = generateSignBlockString(jsonBefore);
        /* 生成生成数字签名的字符串 */
        if (null == signBlock) {
            rglog.error("生成加签Block失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("加签Block:<{}>", signBlock);
        String md5Key = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.WORK_KEY);
        String signData = MD5Util.generateSignString(signBlock, "&key=", md5Key);
        if (null == signData) {
            rglog.error("生成数字签名失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("数字签名:<{}>", signData);

        JSONObject jsonObj = JSON.parseObject(jsonBefore, Feature.OrderedField);
        JSONObject headObj = jsonObj.getJSONObject(OpmsTreeNodeConstant.HEADER);
        headObj.put(OpmsTreeNodeConstant.SIGN, signData);
        jsonObj.put(OpmsTreeNodeConstant.HEADER, headObj);
        rglog.debug("添加signature后发送报文:<{}>", jsonObj.toJSONString());
        return jsonObj.toJSONString().getBytes();
    }

    /**
     * 生成加签Block
     *
     * @param jsonString 原始报文
     * @return 加签BLOCK
     */
    private String generateSignBlockString(String jsonString) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        try {
            if (null != jsonString && 0 != jsonString.trim().length()) {
                rglog.debug("jsonString=[{}]", jsonString);
                JSONObject jsonObject = JSONObject.parseObject(jsonString, Feature.OrderedField);
                Map maps = jsonObject.getInnerMap();
                ArrayList<String> arrayList = new ArrayList<>();
                int i = 0;
                Object key;
                Object value;
                for (Object map : maps.entrySet()) {
                    key = ((Map.Entry) map).getKey();
                    value = ((Map.Entry) map).getValue();
                    if (!"header".equals(key) && null != value) {
                        arrayList.add(key + "=" + value + "&");
                    } else if ("header".equals(key) && null != value) {
                        JSONObject headObj = JSONObject.parseObject(value.toString(), Feature.OrderedField);
                        headObj.remove("signature");
                        arrayList.add(key + "=" + headObj + "&");
                    }
                    i++;
                }
                String[] strArray = arrayList.toArray(new String[0]);
                Arrays.sort(strArray);
                StringBuilder stringBuffer = new StringBuilder();
                for (i = 0; i < strArray.length; i++) {
                    stringBuffer.append(strArray[i]);
                }
                return stringBuffer.substring(0, stringBuffer.length() - 1);
            }
        } catch (Exception e) {
            rglog.error("系统异常!");
            return null;
        }
        return null;
    }
}
